package com.gitee.wsl

import kotlinx.atomicfu.locks.ReentrantLock
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlin.coroutines.Continuation
import kotlin.coroutines.CoroutineContext


actual val Lib.ioDispatcher : CoroutineDispatcher get() = Dispatchers.Default


//actual fun <T> Lib.runBlock(context: CoroutineContext, block: suspend CoroutineScope.() -> T): T
//    = BlockingCoroutine<T>(context =  context).also { block.startCoroutine(Lib.scope,completion = it) }.getValue()

/**
 * Runs a new coroutine and blocks the current thread interruptibly until its completion.
 * Since JavaScript does not support threading, this will just run block on [GlobalScope]
 * @param context the context of the coroutine. The default value is an event loop on the current thread.
 * @param block the coroutine code.
 */
//@OptIn(DelicateCoroutinesApi::class)
//actual fun <T> Lib.runBlock(context: CoroutineContext, block: suspend CoroutineScope.() -> T): T
//       = GlobalScope.async { block(this) }.asDynamic()


private class BlockingCoroutine<T>(override val context: CoroutineContext) : Continuation<T> {
    private val lock = ReentrantLock()
    //private val done = lock.newCondition()

    private var result: Result<T>? = null

    private inline fun <T> locked(block: () -> T): T {
        lock.lock()
        return try {
            block()
        } finally {
            lock.unlock()
        }
    }

    private inline fun loop(block: () -> Unit): Nothing {
        while (true) {
            block()
        }
    }

    override fun resumeWith(result: Result<T>)= locked<Unit> {
        this.result = result
        //done.signal()
    }

    fun getValue(): T = locked<T> {
        loop {
            val result = this.result
            if (result == null) {
                done.awaitUninterruptibly()
            } else {
                return@locked result.getOrThrow()
            }
        }
    }
}
